package com.huterox.spring.framework.webmvc.servlet;

import com.huterox.spring.framework.annotation.*;
import com.huterox.spring.framework.context.HUApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HUDispatcherServlet extends HttpServlet {


    private List<HUHandlerMapping> handlerMappings = new ArrayList<>();
    private Map<HUHandlerMapping,HUHandlerAdapter> handlerAdapterMap = new HashMap<>();
    private List<HUViewResolver> viewResolvers = new ArrayList<>();
    private HUApplicationContext context = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);//保证post/get同时都能处理
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            System.out.println("hello this request will be processed by mymvcframeword ");
            this.doDispatch(req,resp);//相应的处理
        } catch (Exception e) {
            e.printStackTrace();

            Map<String,Object> model = new HashMap<>();

            model.put("detail","500 Exception Detail");
            model.put("stackTrace",Arrays.toString(e.getStackTrace()));

            processDispatchResult(req,resp,new HUModelAndView("500",model));
//            resp.getWriter().write("服务器异常500："+Arrays.toString(e.getStackTrace()));
        }
    }


    @Override
    public void init(ServletConfig config) throws ServletException {

//      IOC DI 部分
        context = new HUApplicationContext(config.getInitParameter("Myproperties"));


        //MVC部分
        //5.初始化HandlerMapping（配置器）
        initStrategies(context);
        //6.完成~~给请求分发器，去去执行对应方法


    }

    protected void initStrategies(HUApplicationContext context){
        //初始化MVC组件

        //未来需要实现组件已注释
        //initMultipartResolver(context) 多文件上传组件
        //initThemeResolver(context) 初始化模板处理器
        //mvc mapping组件(扫描mvc注解参数）
        initHandlerMappings(context);
        // 参数适配器，匹配参数执行方法
        initHandlerAdapters(context);
        //initHandleExceptionResolvers(context) 异常拦截器
        //initRequestToViewNameTranslator(context) 初始化视图预处理器
        // 初始化视图转换器
        initViewResolvers(context);
        // FlashMap处理器initFlashMapManager(context);
    }


    private void initViewResolvers(HUApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = Objects.requireNonNull(this.getClass().getClassLoader().getResource(templateRoot)).getFile();
        File templateRootDir = new File(templateRootPath);
        for (File file : Objects.requireNonNull(templateRootDir.listFiles())) {
            this.viewResolvers.add(new HUViewResolver(templateRoot));
        }

    }

    private void initHandlerAdapters(HUApplicationContext context) {
        //将这个handlermap和adapter建立一对一的关系
        for (HUHandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapterMap.put(handlerMapping,new HUHandlerAdapter());
        }
    }

    private void initHandlerMappings(HUApplicationContext context) {

        if(this.context.getBeanDefinitionCount()==0) return;
        String [] beanNames = this.context.getBeanDefinitionNames();
        //由于我们加入IOC是在那个调用了getBean之后我们才有滴，所以我一开始判断
        //是按照beanDefinition来玩的,有些那个bean是没有Controller之类的注解的，所以IOC不会加入容器，所以没有
        //这个对象，所以注意判断null
        for (String beanName:beanNames) {
            Object instance = this.context.getBean(beanName);
            if(instance==null){
                continue;
            }
            Class<?> clazz = instance.getClass();
            if(!clazz.isAnnotationPresent(HUController.class)) continue;
            String baseURl="";
            if(clazz.isAnnotationPresent(HURequestMapping.class)){
                HURequestMapping requestMapping = clazz.getAnnotation(HURequestMapping.class);
                baseURl = requestMapping.value();
            }
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(HURequestMapping.class))continue;
                HURequestMapping requestMapping = method.getAnnotation(HURequestMapping.class);
                String regex =("/" + baseURl +"/"+ requestMapping.value())
                        .replace("\\*",".*")
                        .replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                handlerMappings.add(new HUHandlerMapping(pattern,instance,method));
                //把url和对应的method放在一起
            }
        }
    }

    private void doInitHandlerMapping() {

    }
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        //1.根据URL找到HandlerMapping对象
        HUHandlerMapping mappingHandler = getHandler(req);
        if(mappingHandler==null){
            //没找到说明没有这个url 返回404
            processDispatchResult(req,resp,new HUModelAndView("404"));
            return;
        }
        //2.根据这个handlermapping 获得一个handlerAdapter
        HUHandlerAdapter ha = getHandlerAdpter(mappingHandler);

        //3.根据这个ha也就是handlerAdapter 的方法去匹配参数，获取返回值
        //如果是modelandview我们就去渲染解析交给后面
        //这个model and view 其实就是模板引擎需要解析的参数和对应的html
        assert ha != null;
        HUModelAndView mv =  ha.handle(req,resp,mappingHandler);

        //4.根据ModelAndView 决定选择哪个ViewResolver去解析渲染
        processDispatchResult(req,resp,mv);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, HUModelAndView mv) throws IOException {
        if(null==mv) return;
        if(this.viewResolvers.isEmpty()) return;
        for (HUViewResolver viewResolver : this.viewResolvers) {
            HUView view = viewResolver.resolverViewName(mv.getViewName());
            view.render(mv.getModel(),req,resp); //返回页面
            return;
        }
    }

    private HUHandlerAdapter getHandlerAdpter(HUHandlerMapping mappingHandler) {
        if(this.handlerAdapterMap.isEmpty()) return null;

        return this.handlerAdapterMap.get(mappingHandler);
    }

    private HUHandlerMapping getHandler(HttpServletRequest req) {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        //tomcat里面配的一开始的那个不要
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        for (HUHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getUrl().matcher(url);
            if(!matcher.matches()) continue;
            return handlerMapping;
        }
        return null;
    }


}
